package com.fzu.express.filters;

import com.fzu.express.entity.WebUser;
import com.fzu.express.enums.RoleEnum;
import com.fzu.express.utils.ApiException;
import com.fzu.express.utils.ErrorEnum;
import com.fzu.express.utils.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/**
 * token校验拦截器
 *
 * @author zyq
 * @date 2022/10/20 12:32
 **/
@Component
public class TokenAuthFilter extends OncePerRequestFilter {

    private final static String ATTRIBUTE_NAME = "exception";

    /**
     * 不进行TOKEN验证的URL
     */
    private final static String ERROR_CONTROLLER = "/api/express/error/handle";
    private final static Set<String> ALLOW_PATH = new HashSet<>();
    private final static HashMap<Integer, HashSet<String>> ROLE_NO_ALLOW_PATH = new HashMap<>(4);
    static {
        // 错误信息处理
        ALLOW_PATH.add(ERROR_CONTROLLER);
        // 登录
        ALLOW_PATH.add("/api/express/web-user/login");
        // 注册
        ALLOW_PATH.add("/api/express/web-user/register");

        // 普通用户和快递员不允许访问的路径
        HashSet<String> userOrCourierSet = new HashSet<>();
        userOrCourierSet.add("/api/express/order/dashboard");
        userOrCourierSet.add("/api/express/type");
        userOrCourierSet.add("/api/express/user");
        ROLE_NO_ALLOW_PATH.put(RoleEnum.USER.getCode(), userOrCourierSet);
        ROLE_NO_ALLOW_PATH.put(RoleEnum.COURIER.getCode(), userOrCourierSet);

        // 网点管理员不允许访问的路径
        HashSet<String> outletSet = new HashSet<>();
        outletSet.add("/api/express/type");
        ROLE_NO_ALLOW_PATH.put(RoleEnum.OUTLET_MANAGER.getCode(), outletSet);


        // 系统管理员不允许访问的路径
        HashSet<String> managerSet = new HashSet<>();
        managerSet.add("/api/express/address-book");
        ROLE_NO_ALLOW_PATH.put(RoleEnum.SYSTEM_MANAGER.getCode(), managerSet);

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String url = request.getRequestURI();
        System.out.println("url => " + url);
        boolean pass = ALLOW_PATH.contains(url);
        // 为不需要TOKEN校验的请求, 直接放行
        if (pass) {
            chain.doFilter(request, response);
            return;
        }
        // 设置编码
        response.setCharacterEncoding("utf-8");

        // 获取请求头中的token
        String token = request.getHeader(JwtUtil.HEADER);

        if (StringUtils.isBlank(token)) {
            request.setAttribute(ATTRIBUTE_NAME,
                    new ApiException(ErrorEnum.NO_HAVE_TOKEN));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        }

        // 进行TOKEN校验
        try {
            JwtUtil.getTokenClaim(token);
        } catch (ExpiredJwtException e) {
            request.setAttribute(ATTRIBUTE_NAME,
                    new ApiException(ErrorEnum.TOKEN_EXPIRED));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        } catch (Exception e) {
            request.setAttribute(ATTRIBUTE_NAME,
                    new ApiException(ErrorEnum.INVALID_TOKEN));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        }

        // 角色权限校验
        WebUser user = JwtUtil.getUser(token);
        HashSet<String> disabledPaths = ROLE_NO_ALLOW_PATH.get(user.getRole());
        if (disabledPaths.contains(url)) {
            request.setAttribute(ATTRIBUTE_NAME,
                    new ApiException(ErrorEnum.NO_PERMISSION));
            request.getRequestDispatcher(ERROR_CONTROLLER)
                    .forward(request, response);
            return;
        }


        chain.doFilter(request, response);
    }
}
